ZeroMQ inproc 传输使用注意
inproc:// 是 ZeroMQ 的进程内传输:消息在进程内存中传递,不经内核网络栈,常用于同进程内线程间的通信(例如 socket monitor 的事件通道就是一个 inproc PAIR)。它有两处容易踩坑的地方:端点命名有明确的唯一性规则,以及一个至今未修复的 socket 关闭 bug(libzmq #4778)。本文整理这两条注意事项,适合在代码里使用 inproc:// endpoint 的开发者。
端点命名规则
依据官方文档 zmq_inproc(7):
- endpoint 形如
inproc://name,name是任意字符串,官方对格式不做其他限制; name在所属 context 内唯一——既不是进程内唯一(不同 context 可以重名),也不是跨进程全局唯一;对同一个 context 同名 bind 两次会得到Address already in use;- 名字最长 256 字符;
- 自 libzmq 4.0 起,connect 与 bind 的先后顺序不再重要(与 tcp 行为一致,connect 先于 bind 也可以)。
「在 context 内唯一」是最容易误解的一条:多个模块各自约定了 inproc://local 这类名字,只要它们共用同一个 zmq_context,后 bind 的就会失败。
socket 关闭影响新 socket 创建(libzmq #4778)
libzmq issue #4778(2026-10 查证仍为 open 状态)报告:inproc 模式下,已打开 socket 的关闭会继续影响新 socket 的创建。
issue 中的最小复现代码:
zmq::context_t ctx;
int count = 0;
while (true) {
try {
zmq::socket_t sock(ctx, zmq::socket_type::pull);
sock.set(zmq::sockopt::linger, 0);
sock.connect("inproc://szn"); // 没有对应的 bind 端点
++count;
} catch (const std::exception &e) {
// 第一轮:count 达 1023 时抛 "Too many open files"
// sleep 5s 后重试:count 为 0——新 socket 立即创建失败
count = 0;
std::this_thread::sleep_for(5s);
}
} 两个值得注意的观察:
- 每个 zmq socket 内部有一个 signaler(Linux 上用
eventfd实现),占用一个 fd;循环里sock每轮迭代离开作用域都会析构,linger也设成了 0,但 fd 计数仍持续增长,在默认 fd 软上限(1024)下大约第 1023 个 socket 就会抛出Too many open files(EMFILE)——析构没有真正释放资源; - 后续轮次 count 为 0,说明在先前 socket 已全部析构的情况下,新 socket 依然立即创建失败。
与 monitor 事件洪泛的区分
Too many open files 在 ZeroMQ 应用里还有一类常见成因:启用了 socket monitor 的程序在连接反复断开重连时,监控事件大量积压,ZMQ IO 线程阻塞在事件发送路径上,socket 析构无法完成,内部的 eventfd 随之泄漏。两者表象相似,排查时值得先区分:如果 fd 增长集中在重连风暴期间、lsof 里堆积的是 eventfd,优先怀疑 monitor 事件洪泛;如果只是单纯的 inproc socket 创建销毁循环,则对应本文的 #4778。
小结
inproc endpoint 的名字按 context 唯一、上限 256 字符来规划,跨模块复用一个 context 时注意命名空间隔离;频繁创建销毁 inproc socket 的场景要留意 #4778——上游未修复,绕开的方式是改用常驻 socket,避免短生命周期 socket 的循环创建。